/**
 * @author : andy
 * @description : sse类
 */
import { merge , omit } from "lodash-es";
import RequestState from "./state";
import { getRequestUrl, isEventStreamType } from "./util";
export type RequestMethodType = 'get' | 'put' | 'post';
export interface ServerSentEventsOptions {
  /** 基础请求路径 */
  baseUrl?: string;
  /** 请求头信息 */
  headers?: Record<string , unknown>;
  /** 请求体 */
  body?: Record<string , unknown>;
  /** 请求超时时间 */
  timeout?: number;
  /** 重连次数 */
  reconnectCount?: number;
  /** 请求路径 */
  url?: string;
  /** 请求方式 */
  method?: RequestMethodType;
  /** 接收消息的回调 */
  onMessage?: (...args: any[]) => void;
  /** 请求失败的回调 */
  onError?: (...args: any[]) => void;
}
export type SSEReturn = {
  data: Record<string , any>;
}
export default class ServerSentEvents {
  /** sse实例唯一标识 */
  id: string;
  /** sse请求参数 */
  private options: ServerSentEventsOptions;
  /** sse请求状态映射 */
  static requestState: RequestState = new RequestState();
  constructor (options: ServerSentEventsOptions = {}) {
    this.id = Date.now().toString(16);
    const defaultConfig: ServerSentEventsOptions = {
      timeout : 30 * 1000,
      reconnectCount : 5,
      headers : {
        'Content-Type' : 'application/json'
      },
      baseUrl : '/',
      method : 'post'
    }
    this.options = merge({} , defaultConfig , omit(options , ['onMessage' , 'onError']));   
  }
  /**
   * post请求
   */
  post (options: ServerSentEventsOptions) {
    const params = merge({} , this.options , options , {method : 'post'});
    let count = 0;
    if (!ServerSentEvents.requestState.get(params)) {
      ServerSentEvents.requestState.add(params , true);
      const xhr = new XMLHttpRequest();
      this._request(xhr , params , count);
      return xhr;
    }
  }
  /**
   * ajax请求
   * @param xhr xhr对象
   * @param options sse请求参数
   * @param count 请求次数
   */
  private _request (xhr: XMLHttpRequest , options: ServerSentEventsOptions , count: number) {
    const {headers , body , method , onMessage , onError , timeout } = options;
    const requestUrl = getRequestUrl(options);
    xhr.open(method! , requestUrl);
    // 设置请求头信息
    this._setHeaders(xhr , headers as Record<string , string>);
    xhr.timeout = timeout as number;
    xhr.ontimeout = () => {
      // 请求超时，重新请求
      this._reRequest(xhr , options , count);
    };
    xhr.onerror = (evt: Event) => {
      ServerSentEvents.requestState.delete(options);
      if (onError) {
        onError(evt);
      }
    };
    xhr.onreadystatechange = () => {
      if (xhr.status === 200) {
        if (xhr.readyState === 3) {
          if (isEventStreamType(xhr.getResponseHeader('Content-Type')!)) {
            // 如果是流数据
            try {
              const message = this._parseResponseText(xhr.responseText);
              if (onMessage) {
                onMessage({data : message})
              }
            } catch (err) {
              // 捕获异常
              console.log(err);
            }
          } else {
            if (xhr.getResponseHeader('Content-Type')?.includes('application/json')) {
              // 如果是普通请求返回的数据
              if (onMessage) {
                onMessage({data : JSON.parse(xhr.responseText)});
              }
            } else {
              if (onError) {
                onError(JSON.parse(xhr.responseText));
              }
            }
          }
        } else if (xhr.readyState === 4) {
          // 请求结束后，设置请求状态为false，表示已经请求完毕，可以再次发起请求
          ServerSentEvents.requestState.delete(options);
        }
      } else {
        if (onError) {
          xhr.responseText ? onError(JSON.parse(xhr.responseText)) : onError();
        }
        ServerSentEvents.requestState.delete(options);
      }
    }
    body && xhr.send(JSON.stringify(body));
  }
  /**
   * 重新发起请求
   * @param xhr xhr对象
   * @param options sse请求参数
   * @param count 请求次数
   */
  private _reRequest (xhr: XMLHttpRequest , options: ServerSentEventsOptions , count: number) {
    const { reconnectCount } = options;
    count++;
    if (count <= (reconnectCount as number)) {
      this._request(xhr , options , count);
    } else {
      ServerSentEvents.requestState.delete(options);
    }
  }
  private _setHeaders (xhr: XMLHttpRequest , headers: Record<string , string>) {
    for (const key in headers) {
      if (headers[key]) {
        xhr.setRequestHeader(key , headers[key]);
      }
    }
  }
  /**
   * 解析响应文本数据
   * @param responseText 响应文本
   */
  private _parseResponseText (responseText: string) {
    const res = responseText.split(/\n\r/).map(item => item.replace(/\n|\r/g , ''));
    if (res.length > 0) {
      let messageList = res[0].replace('data: ' , '').split('data: ');
      messageList = messageList.map(message => {
        if (message !== '[DONE]') {
          return JSON.parse(message);
        }
      }).filter(message => message);
      return messageList;
    }
    return [];
  }
  /**
   * 中止请求
   * @param xhr xhr对象
   */
  abort (xhr: XMLHttpRequest) {
    xhr.abort();
  }
}